//
// Created by liuping on 2019/2/14.
//

#include <vector>
#include <string>
#include "test_base.h"
#include "boost/variant.hpp"

using IntFloatString = boost::variant<int, float, std::string>; // 定义支持int、float、string三个类型，并取一个别名
//初始化一个variant
TEST_F(InitVariant) {

    IntFloatString i = 10;
    ASSERT_EQ(10, boost::get<int>(i) );

    IntFloatString f = 20.0f;
    ASSERT_EQ(20.0f, boost::get<float>(f) );

    IntFloatString s = "hello world";
    ASSERT_EQ("hello world", boost::get<const std::string &>(s));

}

// 使用 visitor 的方式访问
struct AddVisitor : public boost::static_visitor<> {
    void operator() (int &i) const {
        i += 1;
    }
    void operator() (float &f) const {
        f += 1.0f;
    }
    void operator() (std::string &s) const {
        s += " !!!";
    }
};

struct PrintVisitor : public boost::static_visitor<>  {
    template <class T>
    void operator() (T t) const {
        std::cout << t << std::endl;
    }
};


TEST_F(UseVisitor) {
    IntFloatString i = 100;
    IntFloatString f = 200.0f;
    IntFloatString s = "hello world";

    boost::apply_visitor(AddVisitor(), i);
    ASSERT_EQ(101, boost::get<int>(i));
    boost::apply_visitor(PrintVisitor(), i);

    boost::apply_visitor(AddVisitor(),  f);
    ASSERT_EQ(201.0f, boost::get<float>(f));
    boost::apply_visitor(PrintVisitor(), f);

    boost::apply_visitor(AddVisitor(),  s);
    ASSERT_EQ("hello world !!!", boost::get<const std::string &>(s));
    boost::apply_visitor(PrintVisitor(), s);
}


// 最贴切的应用场景，同一个函数，可能返回不同类型的返回值
// 例如一元二次方程的求根公式，根可能是一个，也有可能是两个，有可能没有根
using Two = std::pair<double, double>;
using Roots = boost::variant<Two, double, void*>;

Roots FindRoots(double a, double b, double c)
{
    auto d = b*b-4*a*c;

    if (d > 0.0)
    {
        auto p = sqrt(d);
        return std::make_pair((-b + p) / 2 * a, (-b - p) / 2 * a);
    }
    else if (d == 0.0)
        return (-1*b)/(2*a);
    return nullptr;
}

struct RootPrinterVisitor : public boost::static_visitor<>
{
    void operator()(const Two& roots) const
    {
        std::cout << "2 roots: " << roots.first << " " << roots.second << '\n';
    }
    void operator()(double root) const
    {
        std::cout << "1 root: " << root << '\n';
    }
    void operator()(void *) const
    {
        std::cout << "No real roots found.\n";
    }
};

TEST_F(TestFindRoot) {
    boost::apply_visitor(RootPrinterVisitor(), FindRoots(1, -2, 1)); //(x-1)*(x-1)=0
    boost::apply_visitor(RootPrinterVisitor(), FindRoots(1, -3, 2)); //(x-2)*(x-1)=0
    boost::apply_visitor(RootPrinterVisitor(), FindRoots(1, 0, 2));  //x*x - 2 = 0
}


// 多态
struct Triangle {
    void Draw() const {
        std::cout << "△" << std::endl;
    }
};

struct Circle {
    void Draw() const {
        std::cout << "○" << std::endl;
    }
};

struct DrawVisitor : public boost::static_visitor<> {
    template <class T> void operator() (const T &t) const { t.Draw(); }
};

TEST_F(Polymorphism) {
    using Draw = boost::variant<Triangle, Circle>;
    Draw draw;
    std::vector<Draw> draw_list {Triangle{}, Circle{}, Triangle{}};
    for (const auto &item : draw_list) {
        boost::apply_visitor(DrawVisitor{}, item);
    }
}
TEST_FINSH